package lru

import (
	"container/list"
)

type OnEvictedFunc func(key string, value Value)

type Cache struct {
	l         *list.List
	maxBytes  int64 // 最大缓存大小
	cache     map[string]*list.Element
	usedBytes int64 // 已使用内存大小
	// 值被删除后的回调函数，可以为空
	OnEvicted OnEvictedFunc
}

// Value 用来获取值所使用的字节数量
type Value interface {
	Len() int
}

// 它是双向链表的值类型
type entry struct {
	key   string
	value Value
}

func New(maxBytes int64, onEvicted OnEvictedFunc) *Cache {
	return &Cache{
		cache:     make(map[string]*list.Element),
		maxBytes:  maxBytes,
		OnEvicted: onEvicted,
		usedBytes: 0,
		l:         list.New(),
	}
}
func (c *Cache) Get(key string) (value Value, ok bool) {
	if v, ok2 := c.cache[key]; ok2 {
		c.l.MoveToBack(v)
		value = v.Value.(*entry).value
		ok = true
		return
	}
	return
}
func (c *Cache) RemoveOldest() (ok bool) {
	// 获取队首
	ele := c.l.Front()
	if ele != nil {
		// 删除队列元素
		c.l.Remove(ele)
		ety := ele.Value.(*entry)
		// 删除缓存中的数据
		delete(c.cache, ety.key)
		// 更新当前使用字节数
		c.usedBytes = c.usedBytes - int64(ety.value.Len()) - int64(len(ety.key))
		if c.OnEvicted != nil {
			// 回调函数
			c.OnEvicted(ety.key, ety.value)
		}
		return true
	}
	return
}

func (c *Cache) Set(key string, value Value) {
	var oldValueLen int64
	var oldKeyLen int64
	// 判断是修改还是添加
	if v, ok := c.cache[key]; ok {
		ety := v.Value.(*entry)
		// 之前的value大小
		oldValueLen = int64(ety.value.Len())
		// 之前的key大小
		oldKeyLen = int64(len(key))
		// 更新值
		v.Value = value
		// 移动到队尾（刚刚使用过）
		c.l.MoveToBack(v)
	} else {
		// 添加元素到队尾（刚刚使用过）
		ele := c.l.PushBack(&entry{key: key, value: value})
		c.cache[key] = ele
	}
	// 更新使用大小
	// 新增：加key和value的长度
	// 修改：添加key和value的长度，还得减去之前key和value的长度
	c.usedBytes = c.usedBytes + int64(len(key)) + int64(value.Len()) - (oldValueLen + oldKeyLen)
	// c.maxBytes == 0 表示不限制
	for c.usedBytes > c.maxBytes && c.maxBytes != 0 {
		// 如果存储空间不够，则一直删除最近访问少的
		c.RemoveOldest()
	}
	return
}

// Len 返回链表总长度
func (c *Cache) Len() int {
	return c.l.Len()
}
